Skip to content

Conversation

mordante
Copy link
Member

@mordante mordante commented Apr 6, 2025

This generator has almost identical output to the existing script. Notable differences are

  • conditionally include headers that are not implemented yet
  • removes the synopsis
  • uses 2 spaces indent in # if

There are a few more test macros added that triggered bugs in existing FTM.

@mordante mordante requested a review from a team as a code owner April 6, 2025 15:04
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Apr 6, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 6, 2025

@llvm/pr-subscribers-libcxx

Author: Mark de Wever (mordante)

Changes

This generator has almost identical output to the existing script. Notable differences are

  • conditionally include not yet implemented headers
  • removes the synopsis
  • uses 2 spaces indent in # if

There are a few more test macros added that triggered bugs in existing FTM.


Patch is 43.77 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/134542.diff

10 Files Affected:

  • (modified) libcxx/test/libcxx/feature_test_macro/ftm_metadata.sh.py (+31-5)
  • (added) libcxx/test/libcxx/feature_test_macro/generate_header_test.sh.py (+643)
  • (modified) libcxx/test/libcxx/feature_test_macro/implemented_ftms.sh.py (+8-1)
  • (added) libcxx/test/libcxx/feature_test_macro/implemented_standard_library_headers.sh.py (+32)
  • (modified) libcxx/test/libcxx/feature_test_macro/standard_ftms.sh.py (+13-1)
  • (added) libcxx/test/libcxx/feature_test_macro/standard_library_headers.sh.py (+33)
  • (modified) libcxx/test/libcxx/feature_test_macro/test_data.json (+27-1)
  • (modified) libcxx/test/libcxx/feature_test_macro/version_header.sh.py (+5-3)
  • (modified) libcxx/test/libcxx/feature_test_macro/version_header_implementation.sh.py (+19-3)
  • (modified) libcxx/utils/generate_feature_test_macro_components.py (+281-3)
diff --git a/libcxx/test/libcxx/feature_test_macro/ftm_metadata.sh.py b/libcxx/test/libcxx/feature_test_macro/ftm_metadata.sh.py
index 52696d8bc3605..7cf35b2a21d93 100644
--- a/libcxx/test/libcxx/feature_test_macro/ftm_metadata.sh.py
+++ b/libcxx/test/libcxx/feature_test_macro/ftm_metadata.sh.py
@@ -27,26 +27,52 @@ def setUp(self):
     def test_implementation(self):
         expected = {
             "__cpp_lib_any": Metadata(
-                headers=["any"], test_suite_guard=None, libcxx_guard=None
+                headers=["any"],
+                available_since="c++17",
+                test_suite_guard=None,
+                libcxx_guard=None,
             ),
             "__cpp_lib_barrier": Metadata(
                 headers=["barrier"],
+                available_since="c++20",
                 test_suite_guard="!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)",
                 libcxx_guard="_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC",
             ),
+            "__cpp_lib_clamp": Metadata(
+                headers=["algorithm"],
+                available_since="c++17",
+                test_suite_guard=None,
+                libcxx_guard=None,
+            ),
             "__cpp_lib_format": Metadata(
-                headers=["format"], test_suite_guard=None, libcxx_guard=None
+                headers=["format"],
+                available_since="c++20",
+                test_suite_guard=None,
+                libcxx_guard=None,
             ),
             "__cpp_lib_parallel_algorithm": Metadata(
                 headers=["algorithm", "numeric"],
+                available_since="c++17",
+                test_suite_guard=None,
+                libcxx_guard=None,
+            ),
+            "__cpp_lib_to_chars": Metadata(
+                headers=["charconv"],
+                available_since="c++17",
                 test_suite_guard=None,
                 libcxx_guard=None,
             ),
             "__cpp_lib_variant": Metadata(
-                headers=["variant"], test_suite_guard=None, libcxx_guard=None
+                headers=["variant"],
+                available_since="c++17",
+                test_suite_guard=None,
+                libcxx_guard=None,
             ),
-            "__cpp_lib_missing_FTM_in_older_standard": Metadata(
-                headers=[], test_suite_guard=None, libcxx_guard=None
+            "__cpp_lib_zz_missing_FTM_in_older_standard": Metadata(
+                headers=[],
+                available_since="c++17",
+                test_suite_guard=None,
+                libcxx_guard=None,
             ),
         }
         self.assertEqual(self.ftm.ftm_metadata, expected)
diff --git a/libcxx/test/libcxx/feature_test_macro/generate_header_test.sh.py b/libcxx/test/libcxx/feature_test_macro/generate_header_test.sh.py
new file mode 100644
index 0000000000000..cca5bae8e7e70
--- /dev/null
+++ b/libcxx/test/libcxx/feature_test_macro/generate_header_test.sh.py
@@ -0,0 +1,643 @@
+# ===----------------------------------------------------------------------===##
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+# ===----------------------------------------------------------------------===##
+
+# RUN: %{python} %s %{libcxx-dir}/utils %{libcxx-dir}/test/libcxx/feature_test_macro/test_data.json %t/tests
+
+import os
+import sys
+import unittest
+
+UTILS = sys.argv[1]
+TEST_DATA = sys.argv[2]
+OUTPUT_PATH = sys.argv[3]
+del sys.argv[1:4]
+
+sys.path.append(UTILS)
+from generate_feature_test_macro_components import FeatureTestMacros
+
+
+class Test(unittest.TestCase):
+    def setUp(self):
+        self.ftm = FeatureTestMacros(TEST_DATA)
+        self.maxDiff = None  # This causes the diff to be printed when the test fails
+
+        self.expected = dict(
+            {
+                "algorithm": """\
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by
+// generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <algorithm>
+
+// Test the feature test macros defined by <algorithm>
+
+// clang-format off
+
+#include <algorithm>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 17
+
+#  ifdef __cpp_lib_clamp
+#    error "__cpp_lib_clamp should not be defined before c++17"
+#  endif
+
+#  ifdef __cpp_lib_parallel_algorithm
+#    error "__cpp_lib_parallel_algorithm should not be defined before c++17"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifndef __cpp_lib_clamp
+#    error "__cpp_lib_clamp should be defined in c++17"
+#  endif
+#  if __cpp_lib_clamp != 201603L
+#    error "__cpp_lib_clamp should have the value 201603L in c++17"
+#  endif
+
+#  ifndef __cpp_lib_parallel_algorithm
+#    error "__cpp_lib_parallel_algorithm should be defined in c++17"
+#  endif
+#  if __cpp_lib_parallel_algorithm != 201603L
+#    error "__cpp_lib_parallel_algorithm should have the value 201603L in c++17"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  ifndef __cpp_lib_clamp
+#    error "__cpp_lib_clamp should be defined in c++20"
+#  endif
+#  if __cpp_lib_clamp != 201603L
+#    error "__cpp_lib_clamp should have the value 201603L in c++20"
+#  endif
+
+#  ifndef __cpp_lib_parallel_algorithm
+#    error "__cpp_lib_parallel_algorithm should be defined in c++20"
+#  endif
+#  if __cpp_lib_parallel_algorithm != 201603L
+#    error "__cpp_lib_parallel_algorithm should have the value 201603L in c++20"
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  ifndef __cpp_lib_clamp
+#    error "__cpp_lib_clamp should be defined in c++23"
+#  endif
+#  if __cpp_lib_clamp != 201603L
+#    error "__cpp_lib_clamp should have the value 201603L in c++23"
+#  endif
+
+#  ifndef __cpp_lib_parallel_algorithm
+#    error "__cpp_lib_parallel_algorithm should be defined in c++23"
+#  endif
+#  if __cpp_lib_parallel_algorithm != 201603L
+#    error "__cpp_lib_parallel_algorithm should have the value 201603L in c++23"
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  ifndef __cpp_lib_clamp
+#    error "__cpp_lib_clamp should be defined in c++26"
+#  endif
+#  if __cpp_lib_clamp != 201603L
+#    error "__cpp_lib_clamp should have the value 201603L in c++26"
+#  endif
+
+#  ifndef __cpp_lib_parallel_algorithm
+#    error "__cpp_lib_parallel_algorithm should be defined in c++26"
+#  endif
+#  if __cpp_lib_parallel_algorithm != 201603L
+#    error "__cpp_lib_parallel_algorithm should have the value 201603L in c++26"
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
+
+""",
+                "any": """\
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by
+// generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <any>
+
+// Test the feature test macros defined by <any>
+
+// clang-format off
+
+#include <any>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 17
+
+#  ifdef __cpp_lib_any
+#    error "__cpp_lib_any should not be defined before c++17"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifndef __cpp_lib_any
+#    error "__cpp_lib_any should be defined in c++17"
+#  endif
+#  if __cpp_lib_any != 201606L
+#    error "__cpp_lib_any should have the value 201606L in c++17"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  ifndef __cpp_lib_any
+#    error "__cpp_lib_any should be defined in c++20"
+#  endif
+#  if __cpp_lib_any != 201606L
+#    error "__cpp_lib_any should have the value 201606L in c++20"
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  ifndef __cpp_lib_any
+#    error "__cpp_lib_any should be defined in c++23"
+#  endif
+#  if __cpp_lib_any != 201606L
+#    error "__cpp_lib_any should have the value 201606L in c++23"
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  ifndef __cpp_lib_any
+#    error "__cpp_lib_any should be defined in c++26"
+#  endif
+#  if __cpp_lib_any != 201606L
+#    error "__cpp_lib_any should have the value 201606L in c++26"
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
+
+""",
+                "barrier": """\
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by
+// generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// UNSUPPORTED: no-threads
+
+// <barrier>
+
+// Test the feature test macros defined by <barrier>
+
+// clang-format off
+
+#include <barrier>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 17
+
+#  ifdef __cpp_lib_barrier
+#    error "__cpp_lib_barrier should not be defined before c++20"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifdef __cpp_lib_barrier
+#    error "__cpp_lib_barrier should not be defined before c++20"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  if !defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)
+#    ifndef __cpp_lib_barrier
+#      error "__cpp_lib_barrier should be defined in c++20"
+#    endif
+#    if __cpp_lib_barrier != 201907L
+#      error "__cpp_lib_barrier should have the value 201907L in c++20"
+#    endif
+#  else
+#    ifdef __cpp_lib_barrier
+#      error "__cpp_lib_barrier should not be defined when the requirement '!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)' is not met!"
+#    endif
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  if !defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)
+#    ifndef __cpp_lib_barrier
+#      error "__cpp_lib_barrier should be defined in c++23"
+#    endif
+#    if __cpp_lib_barrier != 201907L
+#      error "__cpp_lib_barrier should have the value 201907L in c++23"
+#    endif
+#  else
+#    ifdef __cpp_lib_barrier
+#      error "__cpp_lib_barrier should not be defined when the requirement '!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)' is not met!"
+#    endif
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  if !defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)
+#    ifndef __cpp_lib_barrier
+#      error "__cpp_lib_barrier should be defined in c++26"
+#    endif
+#    if __cpp_lib_barrier != 299900L
+#      error "__cpp_lib_barrier should have the value 299900L in c++26"
+#    endif
+#  else
+#    ifdef __cpp_lib_barrier
+#      error "__cpp_lib_barrier should not be defined when the requirement '!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)' is not met!"
+#    endif
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
+
+""",
+                "charconv": """\
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by
+// generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <charconv>
+
+// Test the feature test macros defined by <charconv>
+
+// clang-format off
+
+#if __has_include(<charconv>)
+#  include <charconv>
+#endif
+#include "test_macros.h"
+
+#if TEST_STD_VER < 17
+
+#  ifdef __cpp_lib_to_chars
+#    error "__cpp_lib_to_chars should not be defined before c++17"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  if !defined(_LIBCPP_VERSION)
+#    ifndef __cpp_lib_to_chars
+#      error "__cpp_lib_to_chars should be defined in c++17"
+#    endif
+#    if __cpp_lib_to_chars != 201611L
+#      error "__cpp_lib_to_chars should have the value 201611L in c++17"
+#    endif
+#  else
+#    ifdef __cpp_lib_to_chars
+#      error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
+#    endif
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  if !defined(_LIBCPP_VERSION)
+#    ifndef __cpp_lib_to_chars
+#      error "__cpp_lib_to_chars should be defined in c++20"
+#    endif
+#    if __cpp_lib_to_chars != 201611L
+#      error "__cpp_lib_to_chars should have the value 201611L in c++20"
+#    endif
+#  else
+#    ifdef __cpp_lib_to_chars
+#      error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
+#    endif
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  if !defined(_LIBCPP_VERSION)
+#    ifndef __cpp_lib_to_chars
+#      error "__cpp_lib_to_chars should be defined in c++23"
+#    endif
+#    if __cpp_lib_to_chars != 201611L
+#      error "__cpp_lib_to_chars should have the value 201611L in c++23"
+#    endif
+#  else
+#    ifdef __cpp_lib_to_chars
+#      error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
+#    endif
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  if !defined(_LIBCPP_VERSION)
+#    ifndef __cpp_lib_to_chars
+#      error "__cpp_lib_to_chars should be defined in c++26"
+#    endif
+#    if __cpp_lib_to_chars != 201611L
+#      error "__cpp_lib_to_chars should have the value 201611L in c++26"
+#    endif
+#  else
+#    ifdef __cpp_lib_to_chars
+#      error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
+#    endif
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
+
+""",
+                "format": """\
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by
+// generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <format>
+
+// Test the feature test macros defined by <format>
+
+// clang-format off
+
+#include <format>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 17
+
+#  ifdef __cpp_lib_format
+#    error "__cpp_lib_format should not be defined before c++20"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifdef __cpp_lib_format
+#    error "__cpp_lib_format should not be defined before c++20"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  if !defined(_LIBCPP_VERSION)
+#    ifndef __cpp_lib_format
+#      error "__cpp_lib_format should be defined in c++20"
+#    endif
+#    if __cpp_lib_format != 202110L
+#      error "__cpp_lib_format should have the value 202110L in c++20"
+#    endif
+#  else
+#    ifdef __cpp_lib_format
+#      error "__cpp_lib_format should not be defined because it is unimplemented in libc++!"
+#    endif
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  if !defined(_LIBCPP_VERSION)
+#    ifndef __cpp_lib_format
+#      error "__cpp_lib_format should be defined in c++23"
+#    endif
+#    if __cpp_lib_format != 202207L
+#      error "__cpp_lib_format should have the value 202207L in c++23"
+#    endif
+#  else
+#    ifdef __cpp_lib_format
+#      error "__cpp_lib_format should not be defined because it is unimplemented in libc++!"
+#    endif
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  if !defined(_LIBCPP_VERSION)
+#    ifndef __cpp_lib_format
+#      error "__cpp_lib_format should be defined in c++26"
+#    endif
+#    if __cpp_lib_format != 202311L
+#      error "__cpp_lib_format should have the value 202311L in c++26"
+#    endif
+#  else
+#    ifdef __cpp_lib_format
+#      error "__cpp_lib_format should not be defined because it is unimplemented in libc++!"
+#    endif
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
+
+""",
+                "numeric": """\
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by
+// generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <numeric>
+
+// Test the feature test macros defined by <numeric>
+
+// clang-format off
+
+#include <numeric>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 17
+
+#  ifdef __cpp_lib_parallel_algorithm
+#    error "__cpp_lib_parallel_algorithm should not be defined before c++17"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifndef __cpp_lib_parallel_algorithm
+#    error "__cpp_lib_parallel_algorithm should be defined in c++17"
+#  endif
+#  if __cpp_lib_parallel_algorithm != 201603L
+#    error "__cpp_lib_parallel_algorithm should have the value 201603L in c++17"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  ifndef __cpp_lib_parallel_algorithm
+#    error "__cpp_lib_parallel_algorithm should be defined in c++20"
+#  endif
+#  if __cpp_lib_parallel_algorithm != 201603L
+#    error "__cpp_lib_parallel_algorithm should have the value 201603L in c++20"
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  ifndef __cpp_lib_parallel_algorithm
+#    error "__cpp_lib_parallel_algorithm should be defined in c++23"
+#  endif
+#  if __cpp_lib_parallel_algorithm != 201603L
+#    error "__cpp_lib_parallel_algorithm should have the value 201603L in c++23"
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  ifndef __cpp_lib_parallel_algorithm
+#    error "__cpp_lib_parallel_algorithm should be defined in c++26"
+#  endif
+#  if __cpp_lib_parallel_algorithm != 201603L
+#    error "__cpp_lib_parallel_algorithm should have the value 201603L in c++26"
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
+
+""",
+                "variant": """\
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by
+// generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <variant>
+
+// Test the feature test macros defined by <variant>
+
+// clang-format off
+
+#include <variant>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 17
+
+#  ifdef __cpp_lib_variant
+#    error "__cpp_lib_variant should not be defined before c++17"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifndef __cpp_lib_variant
+#    error "__cpp_lib_variant should be defined in c++17"
+#  endif
+#  if __cpp_lib_variant != 202102L
+#    error "__cpp_lib_variant should have the value 202102L in c++17"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  if !defined(_LIBCPP_VERSION)
+#    ifndef __cpp_lib_variant
+#      error "__cpp_lib_variant should be defined in c++20"
+#    endif
+#    if __cpp_lib_variant != 202106L
+#      error "__cpp_lib_variant should have the value 202106L in c++20"
+#    endif
+#  else
+#    ifdef __cpp_lib_variant
+#      error "__cpp_lib_variant should not be defined because it is unimplemented in libc++!"
+#    endif
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  if !defined(_LIBCPP_VERSION)
+#    ifndef __cpp_lib_variant
+#      error "__cpp_lib_variant should be defined in c++23"
+#    endif
+#    if __cpp_lib_variant != 202106L
+#      error "__cpp_lib_variant should have the value 202106L in c++23"
+#    endif
+#  else
+#    ifdef __cpp_lib_variant
+#      error "__cpp_lib_va...
[truncated]

Copy link

github-actions bot commented Apr 6, 2025

⚠️ Python code formatter, darker found issues in your code. ⚠️

You can test this locally with the following command:
darker --check --diff -r HEAD~1...HEAD libcxx/test/libcxx/feature_test_macro/generate_header_test.sh.py libcxx/test/libcxx/feature_test_macro/standard_library_headers.sh.py libcxx/test/libcxx/feature_test_macro/ftm_metadata.sh.py libcxx/test/libcxx/feature_test_macro/implemented_ftms.sh.py libcxx/test/libcxx/feature_test_macro/invalid.sh.py libcxx/test/libcxx/feature_test_macro/is_implemented.sh.py libcxx/test/libcxx/feature_test_macro/standard_ftms.sh.py libcxx/test/libcxx/feature_test_macro/std_dialects.sh.py libcxx/test/libcxx/feature_test_macro/version_header.sh.py libcxx/test/libcxx/feature_test_macro/version_header_implementation.sh.py libcxx/utils/generate_feature_test_macro_components.py
View the diff from darker here.
--- utils/generate_feature_test_macro_components.py	2025-05-18 12:09:15.000000 +0000
+++ utils/generate_feature_test_macro_components.py	2025-05-18 12:12:45.441300 +0000
@@ -2083,10 +2083,11 @@
             )
         )
 
     return "\n\n".join(result)
 
+
 #
 # The templates used to create a FTM test file
 #
 
 
@@ -2402,21 +2403,20 @@
                 if not std in values.keys():
                     result[get_std_number(std)].append({ftm: None})
                     continue
 
                 result[get_std_number(std)].append(
-                        {
-                            ftm: FtmHeaderTest(
-                                values[std],
-                                self.is_implemented(ftm, std),
-                                self.ftm_metadata[ftm].test_suite_guard,
-                            )
-                        }
+                    {
+                        ftm: FtmHeaderTest(
+                            values[std],
+                            self.is_implemented(ftm, std),
+                            self.ftm_metadata[ftm].test_suite_guard,
+                        )
+                    }
                 )
 
         return result
-
 
     def generate_ftm_test(self, std: Std, ftm: Ftm, value: FtmHeaderTest) -> str:
         """Adds a single `ftm` test for C++ `std` based on the status information in `value`.
 
         When std == None this test is generating the TEST_STD_VER < MIN. Where
@@ -2473,13 +2473,11 @@
             return ftm_unavailable_in_dialect.format(
                 ftm=ftm, dialect=self.ftm_metadata[ftm].available_since
             )
 
         if not value.implemented:
-            return ftm_not_implemented.format(
-                ftm=ftm, value=value.value, dialect=std
-            )
+            return ftm_not_implemented.format(ftm=ftm, value=value.value, dialect=std)
 
         if self.ftm_metadata[ftm].test_suite_guard:
             return ftm_conditionally_implemented.format(
                 ftm=ftm,
                 value=value.value,
@@ -2497,11 +2495,11 @@
             self.generate_ftm_test(std, ftm, value)
             for element in data
             for ftm, value in element.items()
         )
 
-    def generate_lit_markup(self, header:str) -> str:
+    def generate_lit_markup(self, header: str) -> str:
         if not header in lit_markup.keys():
             return ""
 
         return "\n".join(f"// {markup}" for markup in lit_markup[header]) + "\n\n"
 
@@ -2509,32 +2507,32 @@
         """Returns the body for the FTM test of a `header`."""
 
         # FTM block before the first Standard that introduced them.
         # This test the macros are not available before this version.
         data = ftm_header_test_file_dialect_block.format(
-                pp_if="if",
-                operator="<",
-                dialect=get_std_number(self.std_dialects[0]),
-                tests=self.generate_header_test_dialect(
-                    None, next(iter(self.header_ftm_data(header).values()))
-                ),
-            )
+            pp_if="if",
+            operator="<",
+            dialect=get_std_number(self.std_dialects[0]),
+            tests=self.generate_header_test_dialect(
+                None, next(iter(self.header_ftm_data(header).values()))
+            ),
+        )
 
         # FTM for all Standards that have FTM defined.
         # Note in libc++ the TEST_STD_VER contains 99 for the Standard
         # in development, therefore the last entry uses a different #elif.
         data += "".join(
-                ftm_header_test_file_dialect_block.format(
-                    pp_if="elif",
-                    operator="==" if std != get_std_number(self.std_dialects[-1]) else ">",
-                    dialect=std
-                    if std != get_std_number(self.std_dialects[-1])
-                    else get_std_number(self.std_dialects[-2]),
-                    tests=self.generate_header_test_dialect(f"c++{std}", values),
-                )
-                for std, values in self.header_ftm_data(header).items()
+            ftm_header_test_file_dialect_block.format(
+                pp_if="elif",
+                operator="==" if std != get_std_number(self.std_dialects[-1]) else ">",
+                dialect=std
+                if std != get_std_number(self.std_dialects[-1])
+                else get_std_number(self.std_dialects[-2]),
+                tests=self.generate_header_test_dialect(f"c++{std}", values),
             )
+            for std, values in self.header_ftm_data(header).items()
+        )
 
         # The final #endif for the last #elif block.
         data += f"\n#endif // TEST_STD_VER > {get_std_number(self.std_dialects[-2])}"
 
         # Generate the test for the requested header.
@@ -2545,11 +2543,11 @@
             include=(
                 ftm_header_test_file_include_conditional.format(header=header)
                 if header in self.__unavailable_headers
                 else ftm_header_test_file_include_unconditional.format(header=header)
             ),
-            data=data
+            data=data,
         )
 
     def generate_header_test_directory(self, path: os.path) -> None:
         """Generates all FTM tests in the directory `path`."""
 
@@ -2573,11 +2571,12 @@
     # Example how to use the new generator to generate the output.
     if False:
         ftm = FeatureTestMacros(
             os.path.join(
                 source_root, "test", "libcxx", "feature_test_macro", "test_data.json"
-            ), headers_not_available
+            ),
+            headers_not_available,
         )
         version_header_path = os.path.join(include_path, "version")
         with open(version_header_path, "w", newline="\n") as f:
             f.write(ftm.version_header)
 

@mordante mordante force-pushed the users/mordante/ftm/is_implemented branch from 14c5af3 to fa5cbcf Compare April 8, 2025 17:41
Base automatically changed from users/mordante/ftm/is_implemented to main April 11, 2025 18:27
@mordante mordante force-pushed the users/mordante/ftm/generate_header_tests branch from 00cf2e7 to da82709 Compare April 12, 2025 13:04
Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with some comments.

@@ -2296,24 +2374,205 @@ def version_header(self) -> str:
)
)

def header_ftm_data(self, header: str) -> Dict[Std, List[Dict[Ftm, FtmHeaderTest]]]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def header_ftm_data(self, header: str) -> Dict[Std, List[Dict[Ftm, FtmHeaderTest]]]:
def test_data(self, header: str) -> Dict[Std, List[Dict[Ftm, FtmHeaderTest]]]:

Then you call this like self.test_data("cstdlib"), which I guess is reasonably readable?

This generator has almost identical output to the existing script.
Notable differences are
- conditionally include not yet implemented headers
- removes the synopsis
- uses 2 spaces indent in `# if`

There are a few more test macros added that triggered bugs in existing
FTM.
@mordante mordante force-pushed the users/mordante/ftm/generate_header_tests branch from 446fd9c to d2f630d Compare May 18, 2025 12:09
@mordante mordante merged commit b12d68e into main May 18, 2025
114 of 134 checks passed
@mordante mordante deleted the users/mordante/ftm/generate_header_tests branch May 18, 2025 16:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants